<?php
/**
 * Extends UserManager for OpenID
 *
 * @package Extensions
 * @version @@VERSION@@
 * @subpackage @@EXTENSIONNAME@@
 * @copyright Damien Lebrun 2007
 * @author Damien Lebrun
 * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GPLv2
 */

if (!defined('IN_VANILLA')) exit();
include_once($Configuration['LIBRARY_PATH'] . 'People/People.Class.UserManager.php');

 /**
  * OpenID extended UserManager Class
  * @package Extension
  * @subpackage @@EXTENSIONNAME@@
  */
 class OpenID_UserManager extends UserManager {

 	/**
 	 * @access private
 	 * @param OpenID_User $SafeUser
 	 * @return unknown
 	 */
 	function _CreateUser(&$SafeUser) {
		// If validation was successful
		if ($this->Context->WarningCollector->Count() == 0) {
			$s = $this->Context->ObjectFactory->NewContextObject($this->Context, 'SqlBuilder');
			$s->SetMainTable('User', 'u');
			$s->AddFieldNameValue('FirstName', $SafeUser->FirstName);
			$s->AddFieldNameValue('LastName', $SafeUser->LastName);
			$s->AddFieldNameValue('Name', $SafeUser->Name);
			$s->AddFieldNameValue('Email', $SafeUser->Email);
			if (!empty($SafeUser->NewPassword)) {
				$s->AddFieldNameValue('Password', $SafeUser->NewPassword, 1, 'md5');
			}
			$s->AddFieldNameValue('DateFirstVisit', MysqlDateTime());
			$s->AddFieldNameValue('DateLastActive', MysqlDateTime());
			$s->AddFieldNameValue('CountVisit', 0);
			$s->AddFieldNameValue('CountDiscussions', 0);
			$s->AddFieldNameValue('CountComments', 0);
			$s->AddFieldNameValue('RoleID',
				$this->Context->Configuration['DEFAULT_ROLE']);
			$s->AddFieldNameValue('StyleID', 0);
			$s->AddFieldNameValue('UtilizeEmail',
				$this->Context->Configuration['DEFAULT_EMAIL_VISIBLE']);
			$s->AddFieldNameValue('Attributes', '');
			$s->AddFieldNameValue('RemoteIp', GetRemoteIp(1));

			$this->DelegateParameters['User'] = &$SafeUser;
			$this->DelegateParameters['SqlBuilder'] = &$s;
			$this->CallDelegate('PreDataInsert');

			$UserID = $this->Context->Database->Insert(
				$s, $this->Name, 'CreateUser',
				'An error occurred while creating a new user.');

			$Urh = $this->Context->ObjectFactory->NewObject(
				$this->Context, 'UserRoleHistory');
			$Urh->UserID = $UserID;
			$Urh->AdminUserID = 0;
			$Urh->RoleID = $this->Context->Configuration['DEFAULT_ROLE'];
			if ($this->Context->Configuration['ALLOW_IMMEDIATE_ACCESS']) {
				$Urh->Notes = $this->Context->GetDefinition('RegistrationAccepted');
			} else {
				$Urh->Notes = $this->Context->GetDefinition('RegistrationPendingApproval');
			}
			$this->AssignRole($Urh, 1);

			if ($SafeUser->OpenIdUrls[0]) {
				$this->AttachOpenID($UserID, $SafeUser->OpenIdUrls[0], 1);
			}

			$this->CallDelegate('PostRoleAssignment');
			return $UserID;
		}
 	}

 	function _NotifyCreation(&$User) {
 		// Notify user administrators
		if (!$this->Context->Configuration['ALLOW_IMMEDIATE_ACCESS']) {
			$s = $this->Context->ObjectFactory->NewContextObject($this->Context, 'SqlBuilder');
			$s->SetMainTable('User', 'u');
			$s->AddJoin('Role', 'r', 'RoleID', 'u', 'RoleID', 'inner join');
			$s->AddWhere('r', 'PERMISSION_RECEIVE_APPLICATION_NOTIFICATION', '', 1, '=');
			$s->AddWhere('u', 'SendNewApplicantNotifications', '', 1, '=');
			$s->AddSelect(array('Name', 'Email'), 'u');
			$Administrators = $this->Context->Database->Select(
				$s, $this->Name, 'CreateUser', 'An error occurred while retrieving administrator email addresses.', 0);
			// Fail silently if an error occurs while notifying administrators

			// Get the email body
			$File = $this->Context->Configuration['LANGUAGES_PATH']
				.$this->Context->Configuration['LANGUAGE']
				.'/email_applicant.txt';
			$EmailBody = @file_get_contents($File);

			if ($EmailBody && $Administrators) {
				$EmailBody = str_replace(
					array(
						"{applicant_name}",
						"{applicant_email}",
						"{application_url}"
					),
					array(
						$User->Name,
						$User->Email,
						ConcatenatePath(
							$this->Context->Configuration['BASE_URL'],
							GetUrl($this->Context->Configuration, 'settings.php', '', '', '', '', 'PostBackAction=Applicants')
						)
					),
					$EmailBody
				);

				$this->DelegateParameters['User'] = &$User;
				$this->DelegateParameters['EmailBody'] = &$EmailBody;
				$this->CallDelegate('PreNewUserNotification');

				if ($this->Context->Database->RowCount($Administrators) > 0) {
					$e = $this->Context->ObjectFactory->NewContextObject($this->Context, 'Email');
					$e->HtmlOn = 0;
					$e->ErrorManager = &$this->Context->ErrorManager;
					$e->WarningCollector = &$this->Context->WarningCollector;
					$e->AddFrom($User->Email, $User->Name);
					$AdminEmail = '';
					$AdminName = '';
					while ($Row = $this->Context->Database->GetRow($Administrators)) {
						$AdminEmail = ForceString($Row['Email'], '');
						$AdminName = ForceString($Row['Name'], '');
						if ($AdminEmail != '') $e->AddRecipient($AdminEmail, $AdminName);
					}
					$e->Subject = $this->Context->GetDefinition("NewApplicant");
					$e->Body = $EmailBody;
					$e->Send(0);
				}
			}
		}
 	}

 	/**
 	 * Create a new user
 	 *
 	 * @param OpenID_User $User
 	 * @return unknown
 	 */
 	function CreateUser(&$User) {
		$SafeUser = clone($User);
		$SafeUser->FormatPropertiesForDatabaseInput();

		// Instantiate a new validator for each field
		Validate($this->Context->GetDefinition('EmailLower'), 1, $SafeUser->Email, 200,
			'^([A-Z0-9+_-][A-Z0-9+_.-]{0,63})@(([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|([A-Z0-9][A-Z0-9.-]{0,244}\.[A-Z]{2,10}))$',
			$this->Context
		);
		Validate($this->Context->GetDefinition('UsernameLower'), 1, $SafeUser->Name, 20, '', $this->Context);
		if (!$SafeUser->AgreeToTerms) $this->Context->WarningCollector->Add($this->Context->GetDefinition('ErrAgreeTOS'));

		if (@$User->OpenIdUrls[0]) {
			$SafeUser->Password = '';

			// Validate OpenID
			Validate(
 				$this->Context->GetDefinition('OpenID Url Format'), 1,
 				$User->OpenIdUrls[0], 255, '^https?://.*$', $this->Context);

 			// The OpenId is not alreacy in use
 			if ($this->GetUserIdByOpenId($User->OpenIdUrls[0])) {
				$this->Context->WarningCollector->Add(
				$this->Context->GetDefinition('You are already register with this user.'));
			}

		} else {
			// Or Validate password
			Validate($this->Context->GetDefinition('PasswordLower'), 1, $SafeUser->NewPassword, 50, '', $this->Context);
			if ($SafeUser->NewPassword != $SafeUser->ConfirmPassword) $this->Context->WarningCollector->Add($this->Context->GetDefinition('ErrPasswordsMatchBad'));
		}

		// Ensure the username isn't taken already
		$s = $this->Context->ObjectFactory->NewContextObject($this->Context, 'SqlBuilder');
		$s->SetMainTable('User', 'u');
		$s->AddSelect('UserID', 'u');
		$s->AddWhere('u', 'Name', '', FormatStringForDatabaseInput($SafeUser->Name), '=');
		$MatchCount = 0;
		$result = $this->Context->Database->Select($s, $this->Name, 'CreateUser', 'A fatal error occurred while validating your input.');
		$MatchCount = $this->Context->Database->RowCount($result);
		if ($MatchCount > 0) $this->Context->WarningCollector->Add($this->Context->GetDefinition('ErrUsernameTaken'));

		$this->DelegateParameters['User'] = &$User;
		$this->CallDelegate('PostValidation');

		// Create User
		$User->UserID = $this->_CreateUser($SafeUser);

		// Notify Admin
		$this->_NotifyCreation($User);

		return $this->Context->WarningCollector->Iif();
	}

 	/**
 	 * Returns a SqlBuilder object with all of the user properties already defined in the select
 	 *
 	 * @return SqlBuilder
 	 */
	function GetUserBuilder() {
		$s = parent::GetUserBuilder();
		$s->AddSelect('OpenId_UseOpenId', 'u', 'UseOpenId');
		return $s;
	}

	/**
	 * Returns a SqlBuilder object with all of the user properties (with the openid_url one)
	 * already defined in the select.
	 *
 	 * @return SqlBuilder
	 */
	function GetUserBuilderWithOpenId(){
		$s = $this->GetUserBuilder();
		$s->AddJoin('OpenID_OpenId', 'o', 'UserID', 'u', 'UserID', 'left join');
		$s->AddSelect('Url', 'o', 'OpenIdUrl');
		return $s;
	}

 	/**
 	 * Get a user by its openID
 	 *
 	 * @param OpenID_User $User
 	 * @return OpenID_User
 	 */
 	function GetUserByOpenID(&$User) {
 		$SafeUser = clone($User);

 		if ($this->Context->WarningCollector->Count() == 0) {
			$s = $this->GetUserBuilderWithOpenId();
			$s->AddWhere('o', 'Url', '', $SafeUser->OpenIdUrls[0], '=');

			$Found = false;
			$UserData = $this->Context->Database->Select(
				$s, $this->Name, 'GetUserByOpenID',
				'An error occurred while attempting to retrieve the requested user.'
			);
			while ($rows = $this->Context->Database->GetRow($UserData)) {
				$SafeUser->GetPropertiesFromDataSet($rows);
				$Found = true;
			}
			return $Found ? $User : false;
 		}

 		return false;
	}

	/**
	 * Get the openID(s) of user
	 *
	 * @param int $UserId
	 * @return array
	 */
	function GetUserByIdWithOpenIds($UserId) {
		if ($UserID > 0) {
			$s = $this->GetUserBuilderWithOpenId();
			$s->AddWhere('u', 'UserID', '', $UserID, '=');

			$Found = false;
			$User = $this->Context->ObjectFactory->NewContextObject($this->Context, 'User');
			$UserData = $this->Context->Database->Select($s, $this->Name, 'GetUserById', 'An error occurred while attempting to retrieve the requested user.');
			while ($rows = $this->Context->Database->GetRow($UserData)) {
				$User->GetPropertiesFromDataSet($rows);
				$Found = true;
			}
			return $Found ? $User : false;
		} else {
			return false;
		}
	}

	/**
	 * Get User (its ID) by its openID Url
	 *
	 * @param string $OpenIdUrl
	 * @return int
	 */
	function GetUserIdByOpenId($OpenIdUrl){

 		if ($this->Context->WarningCollector->Count() == 0) {
			$s = $this->Context->ObjectFactory->NewContextObject($this->Context, 'SqlBuilder');
			$s->SetMainTable('OpenID_OpenId', 'o');
			$s->AddSelect('Url', 'o', 'OpenIdUrl');
			$s->AddSelect('UserID', 'o', 'UserID');
			$s->AddWhere('o', 'Url', '', $OpenIdUrl, '=');

			$OpenIdData = $this->Context->Database->Select(
				$s, $this->Name, 'GetUserIdByOpenId',
				'An error occurred while attempting to retrieve the requested OpenId Url.'
			);
			$UserId = '';
			while ($rows = $this->Context->Database->GetRow($OpenIdData)) {
				$UserId = $rows['UserID'];
			}
			return $UserId;
 		}
 		return false;
	}

	/**
	 * Attach an openID url to a user
	 *
	 * @param int $UserID
	 * @param string $OpenIdUrl
	 * @param bool $IsSafe
	 * @return bool
	 */
	function AttachOpenID($UserID, $OpenIdUrl, $IsSafe) {
		if (!$IsSafe) {
			// Check the user exits
	 		if (!$this->GetUserById($SafeUser->UserID)) {
	 			$this->Context->WarningCollector->Add(
	 				$this->Context->GetDefinition('Could not find the user.')
	 			);
	 			return false;
	 		}
	 		$OpenIdUrl = FormatStringForDatabaseInput($OpenIdUrl, 1);
	 		$UserID = ForceInt($UserID, 0);
		}


		if ( $this->Context->WarningCollector->Count() == 0
			&& $UserID > 0
			&& !empty($OpenIdUrl)
		) {
			$s = $this->Context->ObjectFactory->NewContextObject($this->Context, 'SqlBuilder');
			$s->SetMainTable('OpenID_OpenId', 'o');
			$s->AddFieldNameValue('Url', $OpenIdUrl);
			$s->AddFieldNameValue('UserID', $UserID);
			if ($this->Context->Database->Insert($s, $this->Name, 'AttachOpenIDs',
				'An error occurred while adding the new openID Url.')
			) {
				// Update OpenId_UseOpenId field in the user table
				$s = $this->Context->ObjectFactory->NewContextObject($this->Context, 'SqlBuilder');
				$s->SetMainTable('User', 'u');
				$s->AddFieldNameValue('OpenId_UseOpenId', 1);
				$s->AddWhere('u', 'UserID', '', $UserID, '=');
				if ($this->Context->Database->Update($s, $this->Name, 'AttachOpenIDs',
					'An error occurred while updating OpenId_UseOpenId.')
				) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Detach an openID from a user
	 *
	 * @param string $OpenIdUrl
	 * @param int $UserId
	 * @param bool $UpdateUserTable Should OpenId_UseOpenId be set to 0.
	 */
	function DetachOpenID($OpenIdUrl, $UserId, $UpdateUserTable = 1) {
		$UserId = ForceInt($UserId);
		$OpenIdUrl = FormatStringForDatabaseInput($OpenIdUrl);
		if ($UserId > 0
			&& !empty($OpenIdUrl)
		) {
			// remove url
			$s = $this->Context->ObjectFactory->NewContextObject($this->Context, 'SqlBuilder');
			$s->SetMainTable('OpenID_OpenId', 'o');
			$s->AddWhere('o', 'UserID', '', $UserId, '=');
			$s->AddWhere('o', 'Url', '', $OpenIdUrl, '=');
			$this->Context->Database->Delete($s, $this->Name, 'DetachOpenID',
				"An error occurred while removing the openID Url.");

			if ($UpdateUserTable) {
				// Update OpenId_UseOpenId field in the user table
				$s = $this->Context->ObjectFactory->NewContextObject($this->Context, 'SqlBuilder');
				$s->SetMainTable('User', 'u');
				$s->AddFieldNameValue('OpenId_UseOpenId', 0);
				$s->AddWhere('u', 'UserID', '', $SafeUser->UserID, '=');
				$this->Context->Database->Update($s, $this->Name, 'AttachOpenIDs',
					'An error occurred while updating DetachOpenID.');
			}
		}
	}

 	function ValidateOpenIdAuthentication() {

 		$OpenIdManager =& new OpenID_OpenIdManager($this->Context);
		$OpenId = $OpenIdManager->ProcessAuthResponse();
		if (!$OpenId) {
			$this->Context->WarningCollector->Add(
				$this->Context->GetDefinition('Authentication failed!'));
		} else {
			$s = $this->Context->ObjectFactory->NewContextObject($this->Context, 'SqlBuilder');
			$s->SetMainTable('OpenID_OpenId', 'o');
			$s->AddJoin('User', 'u', 'UserID', 'o', 'UserID', 'inner join');
			$s->AddJoin('Role', 'r', 'RoleID', 'u', 'RoleID', 'left join');
			$s->AddSelect('PERMISSION_SIGN_IN', 'r');
			$s->AddSelect('Url', 'o', 'OpenIdUrl');
			$s->AddSelect('UserID', 'o', 'UserID');
			$s->AddWhere('o', 'Url', '', $OpenId, '=');

			$OpenIdData = $this->Context->Database->Select(
				$s, $this->Name, 'GetUserIdByOpenId',
				'An error occurred while attempting to retrieve the requested OpenId Url.'
			);

			$UserId = 0;
			if (!$OpenIdData) {
				$this->Context->WarningCollector->Add($this->Context->GetDefinition(
							'We could not find any user registered with this openID.'));
			} elseif ($this->Context->Database->RowCount($OpenIdData) > 0) {

				$CanSignIn = 0;
				while ($rows = $this->Context->Database->GetRow($OpenIdData)) {
					$UserId = $rows['UserID'];
					$CanSignIn = ForceBool($rows['PERMISSION_SIGN_IN'], 0);
				}

				if (!$CanSignIn) {
					$this->Context->WarningCollector->Add(
						$this->Context->GetDefinition('ErrNoLogin'));
				} else {

					// Update the user's information
					$this->Context->Authenticator->UpdateLastVisit($UserId, '');

					// 2. Log the user's IP address
					$this->Context->Authenticator->LogIp($UserId);

					// Assign the session value
					$this->Context->Authenticator->AssignSessionUserID($UserId);

					$this->Context->Session->Start($this->Context,
						$this->Context->Authenticator, $UserId);
				}
			}
		}
		return $this->Context->WarningCollector->Iif();
	}

}